#include "Acceptor.h"
#include "EventLoop.h"
#include "InetAddress.h"
#include "Logger.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <error.h>
#include <unistd.h>

// 创建一个非阻塞io
static int createNonblocking()
{
    int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
    if(sockfd < 0)
    {
        LOG_FATAL("%s:%s:%d listen socket create error: %d\n", __FILE__, __FUNCTION__, __LINE__, errno);
    }
    return sockfd;
}

Acceptor::Acceptor(EventLoop* loop, const InetAddress &listenAddr, bool reuseport)
    :loop_(loop)
    , acceptSocket_(createNonblocking())  // 创建socket套接字，返回的是listenfd
    , acceptChannel_(loop, acceptSocket_.fd())
    , listenning_(false)
{
    acceptSocket_.setReuseAddr(true);
    acceptSocket_.setReusePort(true);
    acceptSocket_.bindAddress(listenAddr);  // 绑定套接字和地址，创建的套接字listenfd监听listenAddr指定的ipPort
    // TcpServer::start() → Acceptor::listen() 有新用户连接，要执行一个回调（把客户端和服务器连接用的connfd(服务器端的） =》 打包成channel =》 唤醒subloop）
    // 绑定回调
    // baseloop => 监听到acceptChannel_(listenfd) 有事件发生 => 回调Acceptor::handleRead
    acceptChannel_.setReadCallback(std::bind(&Acceptor::handleRead, this));  // Acceptor只关心新用户连接，拿到通信用的fd就行
}
Acceptor::~Acceptor()
{
    // 将acceptChannel_从baseloop中清除
    acceptChannel_.disableAll();
    acceptChannel_.remove();
}

void Acceptor::listen()
{
    listenning_ = true;
    acceptSocket_.listen();  // listen
    acceptChannel_.enableReading();  // acceptChannel_ => Poller 如果有事件发生，就会调用事先注册的ReadCallback
}

// listenfd 有事件发生了，有新用户连接了
void Acceptor::handleRead()
{
    // handleRead()作为回调函数，在Acceptor的构造函数中，被设置成了acceptChannel_读事件（有新连接到达）的回调函数
    // acceptSocket_ 和 acceptChannel_ 是对应的
    InetAddress peerAddr;
    int connfd = acceptSocket_.accept(&peerAddr);  // connfd - 为客户端新连接创建的套接字号，后续服务端和客户端通信使用
    if(connfd >= 0)
    {
        if(newConnectionCallback_)  // 新连接到达的回调操作
        {
            // 轮询找到subloop，唤醒、分发当前的新客户端的Channel
            newConnectionCallback_(connfd, peerAddr);  // 客户端连接fd、客户端地址信息
        }
        else
        {
            // 客户端新用户连接到了，但是没有回调能处理，直接close
            ::close(connfd);
        }
    }
    else
    {
        LOG_ERROR("%s:%s:%d accept error: %d\n", __FILE__, __FUNCTION__, __LINE__, errno);
        if(errno == EMFILE)  // 当前进程可以打开的文件描述符达到上限 → 可以调整上限 单台服务器已经不足以支撑流量了，可以集群
        {
            LOG_ERROR("%s:%s:%d sockfd reached limit.\n", __FILE__, __FUNCTION__, __LINE__);
        }
    }
}